9
关于路由复用策略网上的文章很多,大多是讲如何实现tab标签切换历史数据,至于如何复用的原理讲的都比较朦胧,代码样例也很难适用各种各样的路由配置,比如懒加载模式下多级嵌套路由出口网上的大部分代码都会报错。
我希望能通过这篇文章把如何复用路由的原理讲明白,让小伙伴能明明白白的实用路由复用策略,文字中有不详实和错误的地方欢迎小伙伴批评指正

对路由复用策略的理解

路由复用策略的是对路由的父级相同节点的组件实例的复用,我们平时看到的多级嵌套路由切换时上层路由出口的实例并不会从新实例化就是因为angular默认的路由复用策略在起作用,而我们从写路由复用策略能实现很多事情,其中之一就是实现
历史路由状态(数据)的存储,即jquery时代的tab页签和iframe实现操作历史的切换。

我一开始认为路由复用策略就是对历史路由数据的复用策略,这个错误的观念导致我对路由复用策略接口方法理解起来异常困难,不知小伙伴和我犯没犯同样的错误。观念正确了,下面就理解起来比较方便了,写路由复用策略也就比较顺手了。

下面是angular默认路由复用策略,每切换一下路由,下面代码都再默默的执行。

export class DefaultRouteReuseStrategy {
    shouldDetach(route) { return false; }
    store(route, detachedTree) { }
    shouldAttach(route) { return false; }
    retrieve(route) { return null; }
    shouldReuseRoute(future, curr) {
        return future.routeConfig === curr.routeConfig;
    }
}

关键概念解释

开始文章前我们先了解几个观念概念
  1. 我们的路由是棵树RouterModule.forRoot(Routes)RouterModule.forChild(Routes)这些配置最后形成一个完整的路由树,路由树有个根是没有routeConfig的,routeConfig是我们写的每个route
  2. 路由节点,一个路径是由几个路由节点组成,有的route配置了component,有的则没有
  3. future下一路由, curr当前路由,切换路由时,我们在下文用future表示下一路由,curr表示当前路由

路由复用策略解析

路由复用策略方法调用顺序

  1. shouldReuseRoute(future, curr)
  2. retrieve(route)
  3. shouldDetach(route)
  4. store(route, detachedTree)
  5. shouldAttach(route)
  6. retrieve,取决一上一步的返回值
  7. store(route, detachedTree),取决第五步

shouldReuseRoute

shouldReuseRoute()决定是否复用路由,根据切换的future curr的节点层级依次调用,返回值为true时表示当前节点层级路由复用,然后继续下一路由节点调用,入参为切换的下一级路由(子级)的future curr路由的节点,返回值为false时表示不在复用路由,并且不再继续调用此方法(future路由不再复用,其子级路由也不会复用,所以不需要再询问下去),root路由节点调用一次,非root路由节点调用两次这个方法,第一次比较父级节点,第二次比较当前节点,

retrieve

retrieve()接上一步奏,当当前层级路由不需要复用的时候,调用一下retrieve方法,其子级路由也会调用一下retrieve方法,如果返回的是null,那么当前路由对应的组件会实例化,这种行为一直持续到末级路由。

shouldDetach

shouldDetach是对上一路由的数据是否实现拆离,其调用开始是当前层级路由不需要复用的时候,即shouldReuseRoute()返回false的时候,如果这时候反回false,将继续到上一路由的下一层级调用shouldDetach,直到返回true或者是最末级路由后才结束对shouldDetach的调用,当返回true时就调用一次store 方法,请看下一步奏

store

store存储路分离出来的上一路由的数据,当 shouldDetach返回true时调用一次,存储应该被分离的那一层的路由的DetachedRouteHandle。注意:无论路由树上多个含有组件component路由节点,能分离出来的只能有一个,被存储的也只能有一个,感觉这种机制对使用场景有很大限制。

shouldAttach

shouldAttach是对当前路由的数据是否实现恢复(附加回来),其调用开始是当前层级路由不需要复用的时候,即shouldReuseRoute()返回false的时候,这和shouldDetach的调用时机很像,但是,并不是所有的路由层级都是有组件实例的,只有包含componentroute才会触发shouldAttach,如果反回false,将继续到当前路由的下一带有component的路由层级调用shouldAttach,直到返回true或者是最末级路由后才结束对shouldAttach
的调用,当返回true时就调用一次retrieve 方法,如果retrieve方法去获取一下当前路由的DetachedRouteHandle,返回一个DetachedRouteHandle,就再调用一次store,再保存一下retrieve返回的DetachedRouteHandle。注意注意:无论路由树上多个含有组件component路由节点,能恢复数据的只能有一个节点,这和shouldDetach是一个套路,对使用场景有很大限制。

总结·这个还是实验性的路由复用策略还是不够强大

路由复用策略这种调用机制对使用场景限制很大 ,比如多级路由出口嵌套就无法实现路由数据缓存。因为多级路由出口嵌套的应用切换路由时,前后路由会包含多个带component的路由节点,而每次对路由的存储和恢复只能存储和恢复某一个节点的component的DetachedRouteHandle,其他路由节点上的component就是被从新实例化。明白这一点后我就放弃了想写一个可以适用任何场景的路由复用策略的想法,如果有小伙伴能解决好这一业务场景,欢迎赐教。

如果这个路由复用策略可以存储一个路由上多个节点的DetachedRouteHandle,和恢复多个节点的DetachedRouteHandle,应该能解决上面是的多级路由出口嵌套场景,但不知道会不会带来别的问题。

一个路由复用策略用例

下面贴一个路由复用策略用例,应该是满足大部分人的业务要求,注意事项:只能是末级路由的缓存,且路由切换的时候路由节点上的component不能超过两个。

import {ActivatedRouteSnapshot, DetachedRouteHandle, Route, RouteReuseStrategy} from "@angular/router";

export class CustomerReuseStrategy implements RouteReuseStrategy {
  static handlers: Map<Route, DetachedRouteHandle> = new Map();
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return !route.firstChild;
  }
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return !!CustomerReuseStrategy.handlers.has(route.routeConfig);
  }
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) {
    return curr.routeConfig === future.routeConfig;
  }
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
     if (!!route.firstChild) {
          return null;
        }
    return CustomerReuseStrategy.handlers.get(route.routeConfig);
  }
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    CustomerReuseStrategy.handlers.set(route.routeConfig, handle);
  }
}

很精简,但是很好用,小伙伴可以根据自己的业务逻辑进行改造。

如果感觉这篇文章对你有帮助,请点个赞吧 👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍


jsure
137 声望4 粉丝

爱学习,爱劳动。